pref(provider):收敛公共契约、配置装配与模型目录边界#209
Conversation
# Conflicts: # README.md # docs/session-persistence-design.md
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
|
/review |
# Conflicts: # internal/provider/openaicompat/response.go
|
其实是:/simplify 这个命令,你再试试 |
|
|
/simplify |
完成本次只处理仍值得修、且不跨出作者约束范围的内容。 已完成:
未做:
本次新增提交: |
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
|
/review |
|
/review -codex |
| return err | ||
| } | ||
| if strings.TrimSpace(c.CurrentModel) == "" { | ||
| if strings.TrimSpace(c.CurrentModel) == "" && selected.Source != ProviderSourceCustom { |
There was a problem hiding this comment.
Allowing current_model to stay empty for a selected custom provider leaves the config in a state that passes Load()/EnsureSelection(), but the runtime still always sends cfg.CurrentModel into provider.Generate(). Custom providers also have an empty DefaultModel, so the first user run now fails later in openaicompat.buildRequest() with model is empty. This regresses the main path from “load-time validation” to “first request blows up” for a freshly added custom provider with no cached catalog.
| } | ||
| var models []providertypes.ModelDescriptor | ||
| if providerCfg.Source == ProviderSourceCustom { | ||
| models, err = s.catalogs.ListProviderModels(ctx, input) |
There was a problem hiding this comment.
SelectProvider() still does a synchronous ListProviderModels() for custom providers. On a cache miss that path resolves discovery config and calls remote model discovery before the provider switch completes, and the TUI invokes it with context.Background(). In practice a slow or hung endpoint can still freeze provider switching indefinitely instead of switching immediately and letting catalog refresh happen in the background.
| @@ -129,6 +129,7 @@ go run ./cmd/neocode | |||
| - **[扩展 Provider](docs/guides/adding-providers.md)** — 如何添加新的模型提供商 | |||
There was a problem hiding this comment.
This README now advertises docs/guides/adding-providers.md as the contributor guide, but that guide still describes the pre-refactor API (internal/provider/openai, driver "openai", ChatRequest/Chat). After this PR the canonical names are internal/provider/openaicompat, driver "openaicompat", and GenerateRequest/Generate. Surfacing the stale guide here will send new provider implementations down a non-compiling path.
| return err | ||
| } | ||
| if strings.TrimSpace(c.CurrentModel) == "" { | ||
| if strings.TrimSpace(c.CurrentModel) == "" && selected.Source != ProviderSourceCustom { |
There was a problem hiding this comment.
Allowing current_model to stay empty for a selected custom provider makes config load succeed, but this PR does not guarantee that field is ever backfilled on a cold start. EnsureSelection() now explicitly accepts the empty state when the custom catalog snapshot is empty, while runtime.Run() still forwards cfg.CurrentModel straight into GenerateRequest, so a freshly added custom provider boots and then fails on the first real request with openai provider: model is empty. That leaves the main run path broken for the exact no-cache case this change is trying to unblock.
| return err | ||
| } | ||
| if strings.TrimSpace(c.CurrentModel) == "" { | ||
| if strings.TrimSpace(c.CurrentModel) == "" && selected.Source != ProviderSourceCustom { |
There was a problem hiding this comment.
Allowing a selected custom provider to keep current_model empty makes startup succeed, but it still leaves the main loop in an unusable state: runtime.Run() forwards cfg.CurrentModel unchanged into GenerateRequest.Model, and the OpenAI-compatible driver still rejects an empty model. Because the async catalog refresh only repopulates the picker and never auto-selects a discovered model, a freshly selected custom provider can boot successfully and then fail on the first user message with model is empty until the user manually picks one. That is still a broken primary flow for the new custom-provider path.
| @@ -731,7 +762,7 @@ func (s *Service) callProviderWithRetry( | |||
| streamDone := make(chan error, 1) | |||
| go s.forwardProviderEvents(ctx, runID, sessionID, streamEvents, streamDone, acc) | |||
There was a problem hiding this comment.
This retry loop resets only the local accumulator before retrying. Any text_delta / tool_call_* events that the first attempt already emitted have already been forwarded to TUI via forwardProviderEvents, so a retry after a mid-stream retryable failure leaves stale partial output on screen and then appends the successful retry on top of it. In practice a transient disconnect after a few streamed tokens will surface duplicated/ghost assistant text even though only the final attempt is persisted. We need either buffered forwarding that is committed only on success, or an explicit rollback/reset event before retrying.
# Conflicts: # internal/provider/errors.go # internal/provider/openaicompat/response.go
变更说明
本 PR 基于
provider-phase2-final-plan的 Phase A / Phase B 目标,对当前项目已落地的 provider 重构工作做统一收口。已完成了 Phase A 的主体:provider 公共契约去 OpenAI Chat 语义、driver 能力模型补齐以及流式 EOF 异常处理稳定化;Phase B 主体:builtin/custom provider 来源管理、远程模型目录缓存、连接身份、共享类型迁移、provider.RuntimeConfig与catalog` 输入边界收口,以及 selection 行为修正。主要改动
Chat语义统一调整为Generate/GenerateRequest,让 runtime 与 driver 的交互模型脱离 OpenAI 专属命名。EOF场景下被误当作正常完成的问题,并补齐对应回归测试。ModelDescriptor、ModelCapabilityHints等 discovery/catalog 共享类型迁移到internal/provider/types,让 provider、catalog、selection、TUI 共享同一套领域模型。provider.ProviderIdentity,按 driver 专属维度生成稳定缓存键,用于 provider 去重和cache/models/*.json远程模型目录缓存。config负责扫描providers/*/provider.yaml、合并 builtin/custom、执行去重与校验,并明确 custom provider 的模型来源只能是远程 discovery 与缓存。provider.RuntimeConfig,让 provider 构建与 discovery 使用 provider-owned 的最小运行时输入,而不是继续直接依赖ResolvedProviderConfig。provider/catalog的输入从过细的 getter 协议进一步收敛为 provider-owned 的具体输入结构,统一承载 identity、默认模型、已配置模型与 lazy discovery resolver。current_model,对于 unsupported driver 改为显式报错,不再静默改写selected_provider。internal/provider/openai目录语义收敛为internal/provider/openaicompat,统一代码层对 OpenAI-compatible 协议的命名表达。预期收益
provider/types、provider.RuntimeConfig与 provider-owned catalog 输入结构共同构成更稳定的跨层契约,降低config/provider/catalog/runtime之间的缠绕。